package cn.ddiancan.xddcloud.psc.flow.config;

import cn.ddiancan.xddcloud.psc.flow.expression.Expression;
import cn.ddiancan.xddcloud.psc.flow.feign.XddExtInstanceRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddHisInstanceRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddHisTaskActorRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddHisTaskRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddInstanceRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddTaskActorRemoteService;
import cn.ddiancan.xddcloud.psc.flow.feign.XddTaskRemoteService;
import cn.ddiancan.xddcloud.psc.flow.parser.ProcessModelParser;
import cn.ddiancan.xddcloud.psc.flow.engine.service.ProcessService;
import cn.ddiancan.xddcloud.psc.flow.engine.service.QueryService;
import cn.ddiancan.xddcloud.psc.flow.engine.service.RuntimeService;
import cn.ddiancan.xddcloud.psc.flow.strategy.TaskAccessStrategy;
import cn.ddiancan.xddcloud.psc.flow.provider.TaskActorProvider;
import cn.ddiancan.xddcloud.psc.flow.interceptor.TaskCreateInterceptor;
import cn.ddiancan.xddcloud.psc.flow.engine.scheduling.TaskReminder;
import cn.ddiancan.xddcloud.psc.flow.engine.service.TaskService;
import cn.ddiancan.xddcloud.psc.flow.trigger.TaskTrigger;
import cn.ddiancan.xddcloud.psc.flow.engine.service.XddcloudEngine;
import cn.ddiancan.xddcloud.psc.flow.engine.cache.FlowCache;
import cn.ddiancan.xddcloud.psc.flow.context.XddcloudContext;
import cn.ddiancan.xddcloud.psc.flow.engine.core.XddcloudEngineImpl;
import cn.ddiancan.xddcloud.psc.flow.handler.ConditionNodeHandler;
import cn.ddiancan.xddcloud.psc.flow.handler.CreateTaskHandler;
import cn.ddiancan.xddcloud.psc.flow.handler.FlowJsonHandler;
import cn.ddiancan.xddcloud.psc.flow.strategy.GeneralAccessStrategy;
import cn.ddiancan.xddcloud.psc.flow.provider.GeneralTaskActorProvider;
import cn.ddiancan.xddcloud.psc.flow.engine.service.impl.ProcessServiceImpl;
import cn.ddiancan.xddcloud.psc.flow.engine.service.impl.QueryServiceImpl;
import cn.ddiancan.xddcloud.psc.flow.engine.service.impl.RuntimeServiceImpl;
import cn.ddiancan.xddcloud.psc.flow.engine.service.impl.TaskServiceImpl;
import cn.ddiancan.xddcloud.psc.flow.engine.listener.InstanceListener;
import cn.ddiancan.xddcloud.psc.flow.engine.listener.TaskListener;
import cn.ddiancan.xddcloud.psc.flow.engine.scheduling.JobLock;
import cn.ddiancan.xddcloud.psc.flow.engine.scheduling.LocalLock;
import cn.ddiancan.xddcloud.psc.flow.feign.XddProcessRemoteService;
import cn.ddiancan.xddcloud.psc.flow.handler.FlowJacksonHandler;
import cn.ddiancan.xddcloud.psc.flow.expression.SpelExpression;
import cn.ddiancan.xddcloud.psc.flow.event.EventInstanceListener;
import cn.ddiancan.xddcloud.psc.flow.event.EventTaskListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * spring boot starter 启动自动配置处理类
 *
 * @since 1.0
 */
@Configuration
@EnableConfigurationProperties(XddcloudProperties.class)
public class XddcloudFlowAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public TaskService taskService(@Autowired(required = false) TaskAccessStrategy taskAccessStrategy,
        @Autowired(required = false) TaskListener taskListener, @Autowired(required = false) TaskTrigger taskTrigger,
        XddInstanceRemoteService instanceService, XddExtInstanceRemoteService extInstanceService,
        XddHisInstanceRemoteService hisInstanceService, XddTaskRemoteService taskService,
        XddTaskActorRemoteService taskActorService, XddHisTaskRemoteService hisTaskService,
        XddHisTaskActorRemoteService hisTaskActorService) {
        return new TaskServiceImpl(taskAccessStrategy, taskListener, taskTrigger, instanceService, extInstanceService,
            hisInstanceService, taskService, taskActorService, hisTaskService, hisTaskActorService);
    }

    @Bean
    @ConditionalOnMissingBean
    public QueryService queryService(XddInstanceRemoteService instanceService,
        XddHisInstanceRemoteService hisInstanceService, XddExtInstanceRemoteService extInstanceService,
        XddTaskRemoteService taskService, XddTaskActorRemoteService taskActorService,
        XddHisTaskRemoteService hisTaskService, XddHisTaskActorRemoteService hisTaskActorService) {
        return new QueryServiceImpl(instanceService, hisInstanceService, extInstanceService, taskService,
            taskActorService, hisTaskService, hisTaskActorService);
    }

    @Bean
    @ConditionalOnMissingBean
    public RuntimeService runtimeService(@Autowired(required = false) InstanceListener instanceListener,
        QueryService queryService, TaskService taskService, XddInstanceRemoteService instanceService,
        XddHisInstanceRemoteService hisInstanceService, XddExtInstanceRemoteService extInstanceService) {
        return new RuntimeServiceImpl(instanceListener, queryService, taskService, instanceService, hisInstanceService,
            extInstanceService);
    }

    @Bean
    @ConditionalOnMissingBean
    public ProcessService processService(RuntimeService runtimeService, XddProcessRemoteService xddProcessService) {
        return new ProcessServiceImpl(runtimeService, xddProcessService);
    }

    @Bean
    @ConditionalOnMissingBean
    public JobLock jobLock() {
        return new LocalLock();
    }

    @Bean
    @ConditionalOnMissingBean
    public Expression expression() {
        return new SpelExpression();
    }

    @Bean
    @ConditionalOnMissingBean
    public TaskAccessStrategy taskAccessStrategy() {
        return new GeneralAccessStrategy();
    }


    @Bean
    @ConditionalOnMissingBean
    public TaskActorProvider taskActorProvider() {
        return new GeneralTaskActorProvider();
    }

    @Bean
    @ConditionalOnMissingBean
    public XddcloudEngine flowLongEngine() {
        return new XddcloudEngineImpl();
    }

    @Bean
    @ConditionalOnMissingBean
    public XddcloudContext flowLongContext(ProcessService processService, QueryService queryService,
        RuntimeService runtimeService, TaskService taskService, Expression expression,
        TaskAccessStrategy taskAccessStrategy, TaskActorProvider taskActorProvider, XddcloudEngine flowLongEngine,
        @Autowired(required = false) FlowCache flowCache,
        @Autowired(required = false) ProcessModelParser processModelParser,
        @Autowired(required = false) FlowJsonHandler flowJsonHandler,
        @Autowired(required = false) ConditionNodeHandler conditionNodeHandler,
        @Autowired(required = false) TaskCreateInterceptor taskCreateInterceptor,
        @Autowired(required = false) CreateTaskHandler createTaskHandler,
        @Autowired(required = false) TaskReminder taskReminder, @Autowired(required = false) TaskTrigger taskTrigger) {
        // 静态注入 Jackson 解析 JSON 处理器
        if (null == flowJsonHandler) {
            flowJsonHandler = new FlowJacksonHandler();
        }
        XddcloudContext.setFlowJsonHandler(flowJsonHandler);
        // 注入 Xddcloud 上下文
        XddcloudContext flc = new XddcloudContext(flowCache, processModelParser);
        flc.setProcessService(processService);
        flc.setQueryService(queryService);
        flc.setRuntimeService(runtimeService);
        flc.setTaskService(taskService);
        flc.setExpression(expression);
        flc.setTaskAccessStrategy(taskAccessStrategy);
        flc.setTaskActorProvider(taskActorProvider);
        flc.setConditionNodeHandler(conditionNodeHandler);
        flc.setTaskCreateInterceptor(taskCreateInterceptor);
        flc.setCreateTaskHandler(createTaskHandler);
        flc.setTaskReminder(taskReminder);
        flc.setTaskTrigger(taskTrigger);
        return flc.build(flowLongEngine);
    }

    /**
     * 注入自定义 TaskListener 实现该方法不再生效
     *
     * @param eventPublisher {@link ApplicationEventPublisher}
     * @return {@link EventTaskListener}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "xddcloud", name = "eventing.task", havingValue = "true")
    public EventTaskListener taskListener(ApplicationEventPublisher eventPublisher) {
        return new EventTaskListener(eventPublisher);
    }

    /**
     * 注入自定义 InstanceListener 实现该方法不再生效
     *
     * @param eventPublisher {@link ApplicationEventPublisher}
     * @return {@link EventInstanceListener}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "xddcloud", name = "eventing.instance", havingValue = "true")
    public EventInstanceListener instanceListener(ApplicationEventPublisher eventPublisher) {
        return new EventInstanceListener(eventPublisher);
    }
}
